package Agent;

import java.io.ObjectInputStream.GetField;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Vector;

import Env.*;

public class GreedyAiAgent extends AbstractAgent {

	private State startState;
	//USED ONLY BY THE DIJEKSTRA FUNCTION
	float[] distance ; 
	//USED ONLY BY THE GETSTATERANK FUNCTION
	HashMap<Car, float[]> memo;
	
	Vector<State> path;
	//open states
	PriorityQueue<State> pq;
	
	
	
	
	public GreedyAiAgent(int start,int goal,Environment env) throws Exception{
		super(start, goal, env);
		startState=env.getState(this.position);
		path= new Vector<State>();
		memo = new HashMap<Car, float[]>();
		
		pq= new PriorityQueue<State>(1000,new Comparator<State>() {
			
			@Override
			public int compare(State o1, State o2) {
					try {
						return getStateRank(o1)-getStateRank(o2)>0?1:getStateRank(o1)-getStateRank(o2)==0?0:-1;
					} catch (Exception e) {
						e.printStackTrace();
					}
					return 0;
			}
		});
		
		pq.add(startState);
		
		if (!getPath()) {
			System.out.println("No Path");
			this.stopFlag=true;
			Main.Main.log.fine("No Path for agent" + this.agentName);
		}
		
	}
	
	
	
	
	
	private boolean getPath() throws Exception{
		while(!pq.isEmpty()){
			State minState= pq.poll();
			if (minState.getPosition()==goal){
				getPathFromGoal(minState);
				return true;
			}else {
				Vector<State> expanded= env.expandState(minState);
		//		System.out.println(expanded.size()+"---------");
				pq.addAll(expanded);
			}
		}
		return false;
		
	
}



	private void getPathFromGoal(State minState) {
			if (minState==startState){
				return;
			}
			path.add(0,minState);
			getPathFromGoal(minState.getPapa());
		}




	
	private Graph getGraphForCar(Car c){
		Graph old=env.getGraph();
		Graph g= new Graph(old,c);
		return g;
	}


	@Override
	protected void chooseCarAndVertex() throws Exception {
		chosenCar= path.elementAt(0).getCar();
		chosenVertex=path.elementAt(0).getPosition();
		path.remove(0);
	}
		
		
	protected float getStateRank(State s) throws Exception{
		
		
			float[] x=memo.get(s.getCar());
			Graph g2=getGraphForCar(s.getCar());
			if (x==null){
				x=dijekstra(this.goal, g2);
				memo.put(s.getCar(),x);
			}
			float y=g2.getEdgeCost(s.getPapa().getPosition(), s.getPosition());
			s.setWork(y);
			return x[s.getPosition()];
		
	}
	
	
	private float[] dijekstra(int end, Graph graph) throws Exception {
		distance = new float[env.getGraph().n+1];
		boolean [] S= new boolean [graph.n+1];
		final int[] papa = new int[graph.n+1];
		
		PriorityQueue<Integer> Q= new PriorityQueue<Integer>(graph.n,new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return distance[o1]-distance[o2]>0?1:distance[o1]-distance[o2]==0?0:-1;
			}
		});
		
		for (int i=1;i<=graph.n;i++){
			if (i==end)
				distance[i]=0;
			else 
				distance[i]=Float.MAX_VALUE;
			
			Q.add(i);
		}
		
		while (!Q.isEmpty()){
			int v=Q.poll();
			Vector<Integer> q= graph.getAllConnected(v);
			for (Integer i:q){
				if (!S[i]){
					float cost=graph.getEdgeCost(v,i);
					if (distance[i]>distance[v]+cost){
						distance[i]=distance[v]+cost;
						papa[i]=v;
					}
				}
			}
			S[v]=true;
		}
		return distance;
	}

	@Override
	public void move() throws Exception {
		super.move();
		if (this.position==this.goal){ 
			Main.Main.log.fine("agent got to target");
			this.stopFlag=true;
	
		}
	}
}
